package cn.apthink.utility;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * Object工具类
 * @author David唐
 * @createdate 2024年1月18日
 */
public class ObjectUtil {
	private static String getMethodName(String fieldName) throws Exception {
		byte[] items = fieldName.getBytes();
		items[0] = (byte) ((char) items[0] - 'a' + 'A');
		return new String(items);
	}

	public static void copyIgnoreNull(Object src, Object dest) {
		try {
			Field fields_obj[] = dest.getClass().getDeclaredFields();
			Field fields_this[] = src.getClass().getDeclaredFields();
			for (Field field : fields_obj) {
				field.setAccessible(true);
				// 遍历this_obj
				for (Field element : fields_this) {
					// 发现相同的field就可以拷�?
					if (element.getName().equals(field.getName())) {
						// 除去private static final的属�?
						if ((Modifier.FINAL + Modifier.STATIC + Modifier.PRIVATE) == field.getModifiers()) {
							break;
						}
						String field_name = field.getName();
						// 获得obj的set方法
						Method m = dest.getClass().getMethod("set" + getMethodName(field_name),
								field.getType());
						Class<?> parameter = field.getType();
						// 当set中的参数和field相同�?,就可以拷�?
						if (parameter.toString().equals(element.getGenericType().toString())) {
							Method this_m = src.getClass().getMethod("get" + getMethodName(field_name));
							if (this_m.invoke(src) != null) {
								m.invoke(dest, this_m.invoke(src));
							}
						}
						break;
					}
				}
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void copy (Object src, Object dest) {
		try {
			List<Field> fields_obj = getFields(dest.getClass());
			List<Field>  fields_this = getFields(src.getClass());
			for (Field field : fields_obj) {
				field.setAccessible(true);
				// 遍历this_obj
				for (Field element : fields_this) {
					// 发现相同的field就可以拷�?
					if (element.getName().equals(field.getName())) {
						// 除去private static final的属�?
						if ((Modifier.FINAL + Modifier.STATIC + Modifier.PRIVATE) == field.getModifiers()) {
							break;
						}
						String field_name = field.getName();
						// 获得obj的set方法
						Method m = dest.getClass().getMethod("set" + getMethodName(field_name),
								field.getType());
						Class<?> parameter = field.getType();
						// 当set中的参数和field相同�?,就可以拷�?
						if (parameter.toString().equals(element.getGenericType().toString())) {
							Method this_m = src.getClass().getMethod("get" + getMethodName(field_name));
							m.invoke(dest, this_m.invoke(src));
						}
						break;
					}
				}
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static String diff(Object src, Object dest) {
		StringBuilder msg = new StringBuilder();
		try {
			Field fields_dest[] = dest.getClass().getDeclaredFields();
			Field fields_src[] = src.getClass().getDeclaredFields();
			for (Field field : fields_dest) {
				field.setAccessible(true);
				for (Field element : fields_src) {
					// 发现相同的field就进行比�?
					if (element.getName().equals(field.getName())) {
						String field_name = field.getName();
						// 获得dest的get方法
						Method dest_m = dest.getClass().getMethod("get" + getMethodName(field_name));
						Class<?> parameter = field.getType();
						// 当参数类型相同时,就可以比�?
						if (parameter.toString().equals(element.getGenericType().toString())) {
							Method src_m = src.getClass().getMethod("get" + getMethodName(field_name));
							Object s = src_m.invoke(src);
							Object d = dest_m.invoke(dest);
							if (s == null && d == null) {
								continue;
							}
							if (s != null && !s.equals(d)) {
								msg.append("," + s.toString() + "=>" + d.toString());
							}
						}
						break;
					}
				}
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return msg.length() > 0 ? msg.substring(1) : null;
	}

	/**
	 * 复制字段的值
	 *
	 * @param source         源对象
	 * @param target         目标对象
	 * @param skipFieldNames 过滤的字段
	 */
	public static void deepcopy(Object source, Object target, String[] skipFieldNames) {
		Set<String> skips = new HashSet<String>();
		if (skipFieldNames != null) {
			skips.addAll(Arrays.asList(skipFieldNames));
		}
		HashMap<String, Field> sourceFieldMap = getAllFields(source);
		HashMap<String, Field> targetFieldMap = getAllFields(target);

		sourceFieldMap.forEach((key, value) -> {
			value.setAccessible(true);
			try {
				if (value.get(source) != null && !skips.contains(key) && targetFieldMap.containsKey(key)) {
					targetFieldMap.get(key).setAccessible(true);
					targetFieldMap.get(key).set(target, value.get(source));
				}
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		});
	}

	/**
	 * 获取对象所有的字段 包括父类
	 *
	 * @param obj 对象
	 * @return 字段名与Field对象的映射map
	 */
	private static HashMap<String, Field> getAllFields(Object obj) {
		@SuppressWarnings("rawtypes")
		Class sourceClass = obj.getClass();
		// 获取对象所有字段 包括父类
		ArrayList<Field> sourceFields = new ArrayList<Field>();
		while (sourceClass != null) {
			sourceFields.addAll(Arrays.asList(sourceClass.getDeclaredFields()));
			sourceClass = sourceClass.getSuperclass();
		}
		// 字段名去重
		HashMap<String, Field> sourceFieldMap = new HashMap<String, Field>(16);
		for (Field field : sourceFields) {
			sourceFieldMap.put(field.getName(), field);
		}
		return sourceFieldMap;
	}

    /**
     * 拷贝数据到新对象（单个）
     *
     * @param source 源实例对象
     * @return 拷贝后的新实例对象
     */
    public static <T> T clone(T source) {
        if (Objects.isNull(source)) {
            return null;
        }
        Class<?> c = source.getClass();
        List<Field> fields = getFields(c);
        return newInstance(source, c, fields);
    }

    /**
     * 拷贝数据到新对象（批量）
     *
     * @param sourceList 源实例对象集合
     * @return 拷贝后的新实例对象集合
     */
    public static <T> List<T> cloneList(List<T> sourceList) {
        if (Objects.isNull(sourceList) || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        Class<?> c = getClass(sourceList);
        if (Objects.isNull(c)) {
            return Collections.emptyList();
        }
        List<Field> fields = getFields(c);
        List<T> ts = new ArrayList<T>();
        for (T t : sourceList) {
            T s = newInstance(t, c, fields);
            if (Objects.nonNull(s)) {
                ts.add(s);
            }
        }
        return ts;
    }

    /**
     * 单个深度拷贝
     *
     * @param source 源实例化对象
     * @param target 目标对象类（如：User.class）
     * @return 目标实例化对象
     */
    public static <T> T clone(Object source, Class<T> target) {
        if (Objects.isNull(source) || Objects.isNull(target)) {
            return null;
        }
        List<Field> sourceFields = getFields(source.getClass());
        List<Field> targetFields = getFields(target);
        T t = null;
        try {
            t = newInstance(source, target, sourceFields, targetFields);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 批量深度拷贝（如果原集合中有null,则自动忽略）
     *
     * @param sourceList 源实例化对象集合
     * @param target     目标对象类（如：User.class）
     * @return 目标实例化对象集合
     */
    public static <T, K> List<K> cloneList(List<T> sourceList, Class<K> target) {
        if (Objects.isNull(sourceList) || sourceList.isEmpty() || Objects.isNull(target)) {
            return Collections.emptyList();
        }
        Class<?> c = getClass(sourceList);
        if (Objects.isNull(c)) {
            return Collections.emptyList();
        }
        List<Field> sourceFields = getFields(c);
        List<Field> targetFields = getFields(target);
        List<K> ks = new ArrayList<K>();
        for (T t : sourceList) {
            if (Objects.nonNull(t)) {
                try {
                    K k = newInstance(t, target, sourceFields, targetFields);
                    ks.add(k);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return ks;
    }

    /**
     * 获取List集合中的类名
     *
     * @param list 对象集合
     * @return 类名
     */
    private static <T> Class<?> getClass(List<T> list) {
        for (T t : list) {
            if (Objects.nonNull(t)) {
                return t.getClass();
            }
        }
        return null;
    }

    /**
     * 实例化同源对象
     *
     * @param source   源对象
     * @param newclass 源对象类名
     * @param fields   源对象属性集合
     * @return 同源新对象
     */
    @SuppressWarnings("unchecked")
    private static <T> T newInstance(T source, Class<?> newclass, List<Field> fields) {
        T t = null;
        try {
            t = (T) newclass.getDeclaredConstructor().newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                field.set(t, field.get(source));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 目标实例化对象
     *
     * @param source       原对实例化象
     * @param target       目标对象类
     * @param sourceFields 源对象字段集合
     * @param targetFields 目标对象属性字段集合
     * @return 目标实例化对象
     */
    private static <T> T newInstance(Object source, Class<T> target, List<Field> sourceFields,
                                     List<Field> targetFields) throws Exception {
        T t = target.getDeclaredConstructor().newInstance();
        if (targetFields.isEmpty()) {
            return t;
        }
        for (Field field : sourceFields) {
            field.setAccessible(true);
            Object o = field.get(source);
            Field sameField = getSameField(field, targetFields);
            if (Objects.nonNull(sameField)) {
                sameField.setAccessible(true);
                sameField.set(t, o);
            }
        }
        return t;
    }

    /**
     * 获取目标对象中同源对象属性相同的属性（字段名称，字段类型一致则判定为相同）
     *
     * @param field  源对象属性
     * @param fields 目标对象属性集合
     * @return 目标对象相同的属性
     */
    private static Field getSameField(Field field, List<Field> fields) {
        String name = field.getName();
        String type = field.getType().getName();
        for (Field f : fields) {
            if (name.equals(f.getName()) && type.equals(f.getType().getName())) {
                return f;
            }
        }
        return null;
    }

    /**
     * 获取一个类中的所有属性（包括父类属性）
     *
     * @param c 类名
     * @return List<Field>
     */
    private static List<Field> getFields(Class<?> c) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] fields = c.getDeclaredFields();
        if (fields.length > 0) {
            fieldList.addAll(Arrays.asList(fields));
        }
        return getSuperClassFields(c, fieldList);
    }

    /**
     * 递归获取父类属性
     *
     * @param o         类名
     * @param allFields 外层定义的所有属性集合
     * @return 父类所有属性
     */
    private static List<Field> getSuperClassFields(Class<?> o, List<Field> allFields) {
        Class<?> superclass = o.getSuperclass();
        if (Objects.isNull(superclass) || Object.class.getName().equals(superclass.getName())) {
            return allFields;
        }
        Field[] fields = superclass.getDeclaredFields();
        if (fields.length == 0) {
            return allFields;
        }
        allFields.addAll(Arrays.asList(fields));
        return getSuperClassFields(superclass, allFields);
    }

}
